package cloud.daodao.storm.dict.service;

import cloud.daodao.storm.common.dict.error.DictError;
import cloud.daodao.storm.common.dict.model.codemap.CodeMapData;
import cloud.daodao.storm.common.dict.model.codemap.CodeMapSearch;
import cloud.daodao.storm.common.dict.model.codemap.CodeMapSelect;
import cloud.daodao.storm.common.dict.model.codemap.CodeMapUpsert;
import cloud.daodao.storm.common.error.AppException;
import cloud.daodao.storm.common.helper.DataHelper;
import cloud.daodao.storm.common.model.PageData;
import cloud.daodao.storm.common.model.PageParam;
import cloud.daodao.storm.common.util.bean.BeanUtil;
import cloud.daodao.storm.dict.constant.CacheConstant;
import cloud.daodao.storm.dict.model.CodeMap;
import cloud.daodao.storm.dict.repository.CodeMapRepository;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.*;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author DaoDao
 **/
@Slf4j
@Service
@Transactional
public class CodeMapService {

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private CodeMapRepository codeMapRepository;

    @Resource
    private DataHelper dataHelper;

    public PageData<CodeMapData> codeMapSearch(PageParam<CodeMapSearch> pageParam) {
        Integer page = pageParam.getPage();
        Integer size = pageParam.getSize();
        CodeMapSearch param = pageParam.getParam();
        List<PageParam.Match> matches = pageParam.getMatches();
        List<PageParam.Order> orders = pageParam.getOrders();
        ExampleMatcher matcher = dataHelper.matcher(matches);
        Sort sort = dataHelper.sort(orders);
        CodeMap entity = new CodeMap();
        BeanUtils.copyProperties(param, entity);
        Example<CodeMap> example = Example.of(entity, matcher);
        Pageable pageable = PageRequest.of(page - 1, size, sort);
        Page<CodeMap> dataPage = codeMapRepository.findAll(example, pageable);
        Long total = dataPage.getTotalElements();
        List<CodeMapData> list = dataPage.getContent().stream().map(data -> {
            CodeMapData codeMapData = new CodeMapData();
            BeanUtils.copyProperties(data, codeMapData);
            return codeMapData;
        }).collect(Collectors.toList());
        return new PageData<>(page, size, total, list);
    }

    public CodeMapData codeMapSelect(CodeMapSelect param) {
        Long id = param.getId();
        String key = CacheConstant.CODE_MAP + id;
        String json = stringRedisTemplate.opsForValue().get(key);
        if (null == json) {
            Optional<CodeMap> optional = codeMapRepository.findById(id);
            if (optional.isEmpty()) {
                throw new AppException(DictError.CODE_MAP_NOT_EXIST, String.valueOf(id));
            }
            json = optional.get().toJson();
            stringRedisTemplate.opsForValue().set(key, json, Duration.ofDays(1L));
        }
        CodeMap entity = new CodeMap().ofJson(json);
        CodeMapData data = new CodeMapData();
        BeanUtils.copyProperties(entity, data);
        return data;
    }

    public CodeMapData codeMapUpsert(CodeMapUpsert param) {
        Long id = param.getId();
        String code = param.getCode();
        String key = param.getKey();
        CodeMap entity;
        if (null == id) {
            entity = new CodeMap();
            entity.setCode(code);
            entity.setKey(key);
            boolean b = codeMapRepository.exists(Example.of(entity));
            if (b) {
                throw new AppException(DictError.CODE_MAP_ALREADY_EXIST, code + ":" + key);
            }
        } else {
            Optional<CodeMap> optional = codeMapRepository.findById(id);
            if (optional.isEmpty()) {
                throw new AppException(DictError.CODE_MAP_NOT_EXIST, String.valueOf(id));
            }
            entity = optional.get();
            CodeMap codeMap = new CodeMap();
            codeMap.setCode(code);
            codeMap.setKey(key);
            Optional<CodeMap> o = codeMapRepository.findOne(Example.of(codeMap));
            if (o.isPresent() && !o.get().getId().equals(id)) {
                throw new AppException(DictError.CODE_MAP_ALREADY_EXIST, code + ":" + key);
            }
            entity.setUpdateTime(LocalDateTime.now());
        }
        BeanUtils.copyProperties(param, entity, BeanUtil.nullProperties(param));
        entity = codeMapRepository.saveAndFlush(entity);

        id = entity.getId();
        stringRedisTemplate.delete(CacheConstant.CODE_MAP + id);

        CodeMapData data = new CodeMapData();
        BeanUtils.copyProperties(entity, data);
        return data;
    }

}
